package cn.uncode.baas.server.resource;

import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.script.ScriptException;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.request.WebRequest;

import cn.uncode.baas.server.constant.Resource;
import cn.uncode.baas.server.exception.MethodNotFoundException;
import cn.uncode.baas.server.exception.ValidateException;
import cn.uncode.baas.server.internal.Executer;
import cn.uncode.baas.server.internal.RequestMap;
import cn.uncode.baas.server.internal.module.IModules;
import cn.uncode.baas.server.internal.module.data.DataParamResolve;
import cn.uncode.baas.server.internal.script.ScriptResult;
import cn.uncode.baas.server.service.IGenericService;
import cn.uncode.baas.server.service.IResterService;
import cn.uncode.baas.server.utils.DataUtils;
import cn.uncode.dal.cache.CacheManager;

/**
 * 通用rest接口
 * 
 * @author ywj
 * 
 */
@Controller
@RequestMapping("/{bucket}/{restName}/{version}")
public class GenericResource extends BaseResource {

    @Autowired
    private IGenericService genericService;

    @Autowired
    private IResterService resterService;

    @Autowired
    private CacheManager cacheManager;

    @Autowired
    private IModules modules;

    @RequestMapping(value = "", method = RequestMethod.GET, produces = { "application/json;charset=UTF-8" })
    public @ResponseBody
    ResponseEntity<Object> getList(@PathVariable("bucket") String bucket, @PathVariable("restName") String restName,
            @PathVariable("version") String version, WebRequest request) throws NoSuchMethodException,
            ValidateException, MethodNotFoundException, ScriptException {
    	RequestMap<String, Object> maps = DataUtils.convertWebRequest2Map(request);
        initContext(bucket, restName, Resource.REQ_METHOD_GET, version, "resource-list", maps);
        ScriptResult scriptResult = null;
        RequestMap<String, Object> head = buildHeadMap(request);
        scriptResult = Executer.execute(bucket, restName, Resource.REQ_METHOD_GET, version, maps, true, modules, head);
        if (scriptResult.getResponse() == null) {
            return new ResponseEntity<Object>(HttpStatus.NO_CONTENT);
        }
        clearContext("resource-list");
        return new ResponseEntity<Object>(scriptResult.getResponse(), HttpStatus.OK);
    }

    @RequestMapping(value = "", method = RequestMethod.POST, consumes = "application/json")
    public ResponseEntity<Object> insert(@PathVariable("bucket") String bucket,
            @PathVariable("restName") String restName, @PathVariable("version") String version,
            @RequestBody Map<String, Object> request, WebRequest webRequest) throws NoSuchMethodException, ValidateException,
            MethodNotFoundException, ScriptException {
        if (request == null) {
            return new ResponseEntity<Object>(HttpStatus.BAD_REQUEST);
        }
        ScriptResult scriptResult = null;
        if (request instanceof Map) {
            scriptResult = singlePost(bucket, restName, version, webRequest, request);
            String location = scriptResult.getLocation();
            if (StringUtils.isNotEmpty(location)) {
                HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                        .getRequest();
                MultiValueMap<String, String> headers = new LinkedMultiValueMap<String, String>();
                String url = getBasePath(req);
                if ("/".equals(location.charAt(0))) {
                    headers.add("Location", url + "/" + location);
                } else {
                    headers.add("Location", url + location);
                }
                return new ResponseEntity<Object>(headers, HttpStatus.OK);
            }
        } else if (request instanceof List) {
            List<Map<String, Object>> requestList = (List<Map<String, Object>>) request;
            for (Map<String, Object> map : requestList) {
                try {
                    singlePost(bucket, restName, version, webRequest, map);
                } catch (Exception e) {
                }
            }
        }

        if (null == scriptResult || scriptResult.getResponse() == null) {
            return new ResponseEntity<Object>(HttpStatus.NO_CONTENT);
        }
        clearContext("resource");
        return new ResponseEntity<Object>(scriptResult.getResponse(), HttpStatus.OK);
    }

    

    @RequestMapping(value = "/{id}", method = RequestMethod.GET, produces = { "application/json;charset=UTF-8" })
    public @ResponseBody
    ResponseEntity<Object> get(@PathVariable("bucket") String bucket, @PathVariable("restName") String restName,
            @PathVariable("version") String version, @PathVariable("id") String id, WebRequest request)
            throws NoSuchMethodException, ValidateException, MethodNotFoundException, ScriptException {
    	RequestMap<String, Object> params = DataUtils.convertWebRequest2Map(request);
        params.put(DataParamResolve.FIELD_OBJECT_ID, id);
        initContext(bucket, restName, Resource.REQ_METHOD_GET, version, "resource", params);
        ScriptResult scriptResult = null;
        RequestMap<String, Object> head = buildHeadMap(request);
        scriptResult = Executer
                .execute(bucket, restName, Resource.REQ_METHOD_GET, version, params, true, modules, head);
        if (scriptResult.getResponse() == null) {
            return new ResponseEntity<Object>(HttpStatus.NO_CONTENT);
        }
        clearContext("resource");
        return new ResponseEntity<Object>(scriptResult.getResponse(), HttpStatus.OK);
    }

    @RequestMapping(value = "/{id}", method = RequestMethod.PUT, consumes = "application/json")
    public ResponseEntity<Object> update(@PathVariable("bucket") String bucket,
            @PathVariable("restName") String restName, @PathVariable("version") String version,
            @PathVariable("id") String id, @RequestBody Map<String, Object> request, WebRequest webRequest)
            throws NoSuchMethodException, ValidateException, MethodNotFoundException, ScriptException {
    	RequestMap<String, Object> params = DataUtils.convertWebRequest(request);
        initContext(bucket, restName, Resource.REQ_METHOD_PUT, version, "resource", params);
        params.put(DataParamResolve.FIELD_OBJECT_ID, id);
        ScriptResult scriptResult = null;
        RequestMap<String, Object> head = buildHeadMap(webRequest);
        scriptResult = Executer.execute(bucket, restName, Resource.REQ_METHOD_PUT, version, params, false, modules,
                head);
        if (scriptResult.getResponse() == null) {
            return new ResponseEntity<Object>(HttpStatus.NO_CONTENT);
        }
        clearContext("resource");
        return new ResponseEntity<Object>(scriptResult.getResponse(), HttpStatus.OK);
    }

    @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
    public ResponseEntity<Object> delete(@PathVariable("bucket") String bucket,
            @PathVariable("restName") String restName, @PathVariable("version") String version,
            @PathVariable("id") String id, WebRequest request) throws NoSuchMethodException, ValidateException,
            MethodNotFoundException, ScriptException {
    	RequestMap<String, Object> params = DataUtils.convertWebRequest2Map(request);
        params.put(DataParamResolve.FIELD_OBJECT_ID, id);
        initContext(bucket, restName, Resource.REQ_METHOD_DELETE, version, "resource", params);
        ScriptResult scriptResult = null;
        RequestMap<String, Object> head = buildHeadMap(request);
        scriptResult = Executer.execute(bucket, restName, Resource.REQ_METHOD_DELETE, version, params, false, modules,
                head);
        if (scriptResult.getResponse() == null) {
            return new ResponseEntity<Object>(HttpStatus.NO_CONTENT);
        }
        clearContext("resource");
        return new ResponseEntity<Object>(scriptResult.getResponse(), HttpStatus.OK);
    }

    /**
     * 
     * @param bucket
     * @param restName
     * @param version
     * @param webRequest
     * @param requestMap
     * @return
     * @throws NoSuchMethodException
     * @throws ScriptException
     */
    private ScriptResult singlePost(String bucket, String restName, String version, WebRequest webRequest,
            Map<String, Object> requestMap) throws NoSuchMethodException, ScriptException {
        ScriptResult scriptResult;
        RequestMap<String, Object> maps = DataUtils.convertWebRequest(requestMap);
        initContext(bucket, restName, Resource.REQ_METHOD_POST, version, "resource", maps);
        RequestMap<String, Object> head = buildHeadMap(webRequest);
        scriptResult = Executer
                .execute(bucket, restName, Resource.REQ_METHOD_POST, version, maps, false, modules, head);
        return scriptResult;
    }

    private RequestMap<String, Object> buildHeadMap(WebRequest request) {
        Iterator<String> names = request.getHeaderNames();
        RequestMap<String, Object> headMap = new RequestMap<String, Object>();
        while (names.hasNext()) {
            String name = names.next();
            String[] values = request.getHeaderValues(name);
            StringBuffer sb = new StringBuffer();
            for (String value : values) {
                sb.append(value).append(",");
            }
            if (sb.length() > 0) {
                headMap.put(name, sb.deleteCharAt(sb.lastIndexOf(",")).toString());
            }
        }
        return headMap;
    }

}
